package com.opensource.svgaplayer;

import com.opensource.svgaplayer.iml.ParseCompletion;
import com.opensource.svgaplayer.utils.AttrUtils;
import com.opensource.svgaplayer.utils.Intrinsics;
import com.opensource.svgaplayer.utils.SVGARange;

import ohos.agp.animation.Animator;
import ohos.agp.animation.AnimatorValue;
import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.components.Image;
import ohos.agp.render.Canvas;
import ohos.agp.utils.TextTool;
import ohos.app.Context;

import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;
import ohos.multimodalinput.event.MmiPoint;
import ohos.multimodalinput.event.TouchEvent;

import java.lang.ref.WeakReference;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

/**
 * ex.
 *
 * @author wl
 * @since 2021-06-28
 */
public class SVGAImageView extends Image implements Component.DrawTask, Component.TouchEventListener,
        Component.BindStateChangedListener, Component.EstimateSizeListener {
    private long startTime;
    private int fps;
    boolean isAnimating = false;
    int loops = 0;
    boolean clearsAfterStop = true;
    boolean clearsAfterDetached = true;
    FillMode fillMode = FillMode.Forward;
    SVGACallback callback = null;
    private AnimatorValue mAnimator = null;
    private SVGAClickAreaListener mItemClickAreaListener = null;
    private boolean mAntiAlias = true;
    private boolean mAutoPlay = true;
    private AnimatorListener mAnimatorListener = new AnimatorListener(this);
    private AnimatorUpdateListener mAnimatorUpdateListener = new AnimatorUpdateListener(this);
    private int mStartFrame = 0;
    private int mEndFrame = 0;
    private SVGADrawable svgaDrawable;

    /**
     * onEstimateSize
     *
     * @param i  int
     * @param i1 int
     * @return boolean
     */
    @Override
    public boolean onEstimateSize(int i, int i1) {
        int width = EstimateSpec.getSize(i);
        int height = EstimateSpec.getSize(i1);
        setEstimatedSize(EstimateSpec.getChildSizeWithMode(width, width, EstimateSpec.NOT_EXCEED),
                EstimateSpec.getChildSizeWithMode(height, height, EstimateSpec.NOT_EXCEED));
        return true;
    }

    public enum FillMode {
        Backward,
        Forward,
    }

    /**
     * SVGAImageView
     *
     * @param context Context
     */
    public SVGAImageView(Context context) {
        super(context);
        init(null);
    }

    /**
     * SVGAImageView
     *
     * @param context Context
     * @param attrs   AttrSet
     */
    public SVGAImageView(Context context, AttrSet attrs) {
        super(context, attrs);
        init(attrs);
    }

    /**
     * SVGAImageView
     *
     * @param context      Context
     * @param attrs        AttrSet
     * @param defStyleAttr String
     */
    public SVGAImageView(Context context, AttrSet attrs, String defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(attrs);
    }

    /**
     * setOnAnimKeyClickListener
     *
     * @param svgaClickAreaListener SVGAClickAreaListener
     */
    public void setOnAnimKeyClickListener(SVGAClickAreaListener svgaClickAreaListener) {
        this.mItemClickAreaListener = svgaClickAreaListener;
    }

    /**
     * isAnimating
     *
     * @return boolean
     */
    public final boolean isAnimating() {
        return this.isAnimating;
    }

    /**
     * getLoops
     *
     * @return int
     */
    public final int getLoops() {
        return this.loops;
    }

    /**
     * setLoops
     *
     * @param var1 int
     */
    public final void setLoops(int var1) {
        this.loops = var1;
    }

    /**
     * getClearsAfterStop
     *
     * @return boolean
     */
    public final boolean getClearsAfterStop() {
        return this.clearsAfterStop;
    }

    /**
     * setClearsAfterStop
     *
     * @param var1 boolean
     */
    public final void setClearsAfterStop(boolean var1) {
        this.clearsAfterStop = var1;
    }

    /**
     * getClearsAfterDetached
     *
     * @return boolean
     */
    public final boolean getClearsAfterDetached() {
        return this.clearsAfterDetached;
    }

    /**
     * setClearsAfterDetached
     *
     * @param var1 boolean
     */
    public final void setClearsAfterDetached(boolean var1) {
        this.clearsAfterDetached = var1;
    }

    /**
     * getFillMode
     *
     * @return SVGAImageView.FillMode
     */
    public final SVGAImageView.FillMode getFillMode() {
        return this.fillMode;
    }

    /**
     * setFillMode
     *
     * @param var1 SVGAImageView.FillMode
     */
    public final void setFillMode(SVGAImageView.FillMode var1) {
        Intrinsics.checkParameterIsNotNull(var1, "<set-?>");
        this.fillMode = var1;
    }

    /**
     * getCallback
     *
     * @return SVGACallback
     */
    public final SVGACallback getCallback() {
        return this.callback;
    }

    /**
     * setCallback
     *
     * @param var1 SVGACallback
     */
    public final void setCallback(SVGACallback var1) {
        this.callback = var1;
    }

    private void init(AttrSet attrs) {
        /*if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) {
            this.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
        }*/
        addDrawTask(this::onDraw);
        setTouchEventListener(this::onTouchEvent);
        setBindStateChangedListener(this);
        try {
            if (attrs != null) {
                loadAttrs(attrs);
            }
        } catch (MalformedURLException e) {
            e.printStackTrace();
        }
    }

    private void loadAttrs(AttrSet attrs) throws MalformedURLException {
        loops = AttrUtils.getIntegerFromAttr(attrs, "loopCount", 0);//.getInt(R.styleable.SVGAImageView_loopCount, 0);
        clearsAfterStop = AttrUtils.getBooleanFromAttr(attrs, "clearsAfterStop", true);
        clearsAfterDetached = AttrUtils.getBooleanFromAttr(attrs, "clearsAfterDetached", true);
        mAntiAlias = AttrUtils.getBooleanFromAttr(attrs, "antiAlias", true);
        mAutoPlay = AttrUtils.getBooleanFromAttr(attrs, "autoPlay", true);
        String it = AttrUtils.getStringFromAttr(attrs, "fillMode", "1");
        if (it == "0") {
            fillMode = FillMode.Backward;
        } else if (it == "1") {
            fillMode = FillMode.Forward;
        }
        String source = AttrUtils.getStringFromAttr(attrs, "source", "");
        parserSource(source);
    }

    private void parserSource(String source) throws MalformedURLException {
        if (TextTool.isNullOrEmpty(source)) {
            throw new IllegalArgumentException("source should bot be null");
        }
        WeakReference<SVGAImageView> refImgView = new WeakReference<>(this);
        SVGAParser parser = new SVGAParser(getContext());
        if (source.startsWith("http://") || source.startsWith("https://")) {
            parser.decodeFromURL(new URL(source), createParseCompletion(refImgView));
        } else {
            parser.decodeFromAssets(source, createParseCompletion(refImgView));
        }
    }

    private ParseCompletion createParseCompletion(WeakReference ref) {
        return (new ParseCompletion() {
            public void onComplete(SVGAVideoEntity videoItem) {
                SVGAImageView svgaImageView = (SVGAImageView) ref.get();
                if (svgaImageView != null) {
                    svgaImageView.startAnimation(videoItem);
                }
            }

            public void onError() {
            }
        });
    }

    private void startAnimation(SVGAVideoEntity videoItem) {
        new EventHandler(EventRunner.getMainEventRunner()).postTask(new Runnable() {
            @Override
            public void run() {
                videoItem.setAntiAlias(mAntiAlias);
                SVGADrawable svgaDrawable = getSVGADrawable();
                setVideoItem(videoItem);
                if (svgaDrawable != null) {
                    svgaDrawable.setScaleType(getScaleMode());
                }
                if (mAutoPlay) {
                    startAnimation();
                }
            }
        });
    }

    /**
     * onDraw
     *
     * @param component Component
     * @param canvas    Canvas
     */
    @Override
    public void onDraw(Component component, Canvas canvas) {
        SVGADrawable svgaDrawable = getSVGADrawable();
        if (svgaDrawable != null) {
            int width = getWidth();
            int height = getHeight();
            svgaDrawable.draw(canvas, width, height);
        }
    }

    private void play(SVGARange range, boolean reverse) {
        SVGADrawable drawable = this.getSVGADrawable();
        if (drawable != null) {
            this.setupDrawable();
            this.mStartFrame = Math.max(0, range != null ? range.getLocation() : 0);
            SVGAVideoEntity videoItem = drawable.getVideoItem();
            this.mEndFrame = Math.min(videoItem.frames - 1, (range != null ? range.getLocation() : 0) + (range != null ? range.getLength() : Integer.MAX_VALUE) - 1);
            AnimatorValue animator = new AnimatorValue(); //ValueAnimator.ofInt(this.mStartFrame, this.mEndFrame);
            animator.setCurveType(Animator.CurveType.LINEAR);
            Intrinsics.checkExpressionValueIsNotNull(animator, "animator");
            long duration = (this.mEndFrame - this.mStartFrame + 1) * (1000 / videoItem.FPS);
            animator.setDuration(duration);
            animator.setLoopedCount(this.loops <= 0 ? 99999 : this.loops - 1);
            animator.setValueUpdateListener(mAnimatorUpdateListener);
            fps = videoItem.FPS;
            animator.setStateChangedListener(mAnimatorListener);
            if (reverse) {
                // animator.reverse(); 没有这个方法
            } else {
                animator.start();
                startTime = System.currentTimeMillis();
            }
            this.mAnimator = animator;
        }
    }

    private final void setupDrawable() {
        SVGADrawable drawable = this.getSVGADrawable();
        if (drawable != null) {
            drawable.setCleared(false, this);
            drawable.setScaleType(getScaleMode());
        }
    }

    private SVGADrawable getSVGADrawable() {
        return svgaDrawable;
    }

    /**
     * setVideoItem
     *
     * @param videoItem SVGAVideoEntity
     */
    public void setVideoItem(SVGAVideoEntity videoItem) {
        setVideoItem(videoItem, new SVGADynamicEntity());
    }

    /**
     * setVideoItem
     *
     * @param videoItem   SVGAVideoEntity
     * @param dynamicItem SVGADynamicEntity
     */
    public void setVideoItem(SVGAVideoEntity videoItem, SVGADynamicEntity dynamicItem) {
        if (videoItem == null) {
            setPixelMap(null);
        } else {
            svgaDrawable = new SVGADrawable(videoItem, dynamicItem);
            svgaDrawable.setCleared(clearsAfterStop, this);
            setPixelMap(getPixelMap());
        }
    }

    /**
     * startAnimation
     */
    public void startAnimation() {
        this.startAnimation(null, false);
    }

    /**
     * startAnimation
     *
     * @param range   SVGARange
     * @param reverse boolean
     */
    public void startAnimation(SVGARange range, boolean reverse) {
        this.stopAnimation(false);
        this.play(range, reverse);
    }

    /**
     * stopAnimation
     */
    public void stopAnimation() {
        stopAnimation(clearsAfterStop);
    }

    /**
     * stopAnimation
     *
     * @param clear boolean
     */
    public void stopAnimation(boolean clear) {
        if (mAnimator != null) {
            mAnimator.cancel();
            mAnimator.setStateChangedListener(null);
            mAnimator.setValueUpdateListener(null);
        }
        SVGADrawable svgaDrawable = getSVGADrawable();
        if (svgaDrawable != null) {
            svgaDrawable.stop();
            svgaDrawable.setCleared(clear, this);
        }
    }

    /**
     * onComponentBoundToWindow
     *
     * @param component Component
     */
    @Override
    public void onComponentBoundToWindow(Component component) {
    }

    /**
     * onComponentUnboundFromWindow
     *
     * @param component Component
     */
    @Override
    public void onComponentUnboundFromWindow(Component component) {
        stopAnimation(true);
        if (clearsAfterDetached) {
            clear();
        }
    }

    /**
     * 清除绘制数据，停止动画
     */
    public final void clear() {
        SVGADrawable svgaDrawable = this.getSVGADrawable();
        if (svgaDrawable != null) {
            svgaDrawable.setCleared(true, this);
            svgaDrawable.clear();
        }
        this.setPixelMap(null);
    }

    private int oldFrame = -1;

    private final void onAnimatorUpdate(AnimatorValue animator, float v) {
        long current = System.currentTimeMillis();
        long time = (current - startTime) % animator.getDuration();
        int currentFrame = (int) ((time / (1000 / fps)));
        SVGADrawable svgaDrawable = this.getSVGADrawable();
        if (svgaDrawable != null && currentFrame != oldFrame) {

            oldFrame = currentFrame;
            SVGADrawable drawable = svgaDrawable;
            drawable.setCurrentFrame(currentFrame, this);
            double percentage = (double) (drawable.getCurrentFrame() + 1) / (double) drawable.getVideoItem().frames;
            SVGACallback svgaCallback = this.callback;
            if (svgaCallback != null) {
                svgaCallback.onStep(drawable.getCurrentFrame(), percentage);
            }
        }
    }

    private final void onAnimationEnd(Animator animation) {
        this.isAnimating = false;
        this.stopAnimation();
        SVGADrawable drawable = this.getSVGADrawable();
        if (!this.clearsAfterStop && drawable != null) {
            if (this.fillMode == FillMode.Backward) {
                drawable.setCurrentFrame(this.mStartFrame, this);
            } else if (this.fillMode == FillMode.Forward) {
                drawable.setCurrentFrame(this.mEndFrame, this);
            }
        }
        if (this.clearsAfterStop) {
            if (animation == null) {
                throw new IllegalArgumentException("null cannot be cast to non-null type");
            }
            if (((AnimatorValue) animation).getLoopedCount() <= 0) {
                this.clear();
            }
        }
        if (callback != null) {
            callback.onFinished();
        }
    }

    /**
     * pauseAnimation
     */
    public void pauseAnimation() {
        stopAnimation(false);
        if (callback != null) {
            callback.onPause();
        }
    }

    /**
     * stepToFrame
     *
     * @param frame   int
     * @param andPlay boolean
     */
    public void stepToFrame(int frame, boolean andPlay) {
        pauseAnimation();
        SVGADrawable currentSvgaDrawable = getSVGADrawable();
        if (currentSvgaDrawable == null) {
            return;
        }
        if (andPlay) {
            startAnimation();
        }
        if (frame > currentSvgaDrawable.getVideoItem().frames) {
            frame = frame % currentSvgaDrawable.getVideoItem().frames;
        }
        currentSvgaDrawable.setCurrentFrame(frame, this);
    }

    private static final class AnimatorListener implements Animator.StateChangedListener {
        private WeakReference<SVGAImageView> weakReference;

        /**
         * onAnimationRepeat
         *
         * @param animation Animator
         */
        public void onAnimationRepeat(Animator animation) {
            SVGAImageView var10000 = (SVGAImageView) this.weakReference.get();
            if (var10000 != null) {
                SVGACallback var2 = var10000.getCallback();
                if (var2 != null) {
                    var2.onRepeat();
                }
            }
        }

        /**
         * AnimatorListener
         *
         * @param view SVGAImageView
         */
        public AnimatorListener(SVGAImageView view) {
            super();
            Intrinsics.checkParameterIsNotNull(view, "view");
            this.weakReference = new WeakReference(view);
        }

        /**
         * onStart
         *
         * @param animator Animator
         */
        @Override
        public void onStart(Animator animator) {
            SVGAImageView svgaImageView = this.weakReference.get();
            if (svgaImageView != null) {
                svgaImageView.isAnimating = true;
            }
        }

        /**
         * onStop
         *
         * @param animator Animator
         */
        @Override
        public void onStop(Animator animator) {
        }

        /**
         * onCancel
         *
         * @param animator Animator
         */
        @Override
        public void onCancel(Animator animator) {
            SVGAImageView svgaImageView = this.weakReference.get();
            if (svgaImageView != null) {
                svgaImageView.isAnimating = false;
            }
        }

        /**
         * onEnd
         *
         * @param animator Animator
         */
        @Override
        public void onEnd(Animator animator) {
            SVGAImageView svgaImageView = this.weakReference.get();
            if (svgaImageView != null) {
                svgaImageView.onAnimationEnd(animator);
            }
        }

        /**
         * onPause
         *
         * @param animator Animator
         */
        @Override
        public void onPause(Animator animator) {

        }

        /**
         * onResume
         *
         * @param animator Animator
         */
        @Override
        public void onResume(Animator animator) {

        }
    }

    private static final class AnimatorUpdateListener
            implements AnimatorValue.ValueUpdateListener {
        private final WeakReference weakReference;

        public AnimatorUpdateListener(SVGAImageView view) {
            Intrinsics.checkParameterIsNotNull(view, "view");
            this.weakReference = new WeakReference(view);
        }

        @Override
        public void onUpdate(AnimatorValue animatorValue, float v) {
            SVGAImageView svgaImageView = (SVGAImageView) this.weakReference.get();
            if (svgaImageView != null) {
                svgaImageView.onAnimatorUpdate(animatorValue, v);
            }
        }
    }

    /**
     * onTouchEvent
     *
     * @param component  Component
     * @param touchEvent TouchEvent
     * @return boolean
     */
    @Override
    public boolean onTouchEvent(Component component, TouchEvent touchEvent) {
        if (touchEvent != null) {
            if (touchEvent.getAction() == TouchEvent.PRIMARY_POINT_DOWN) {
                SVGADrawable SVGADrawable1 = this.getSVGADrawable();
                if (SVGADrawable1 != null) {
                    int index = touchEvent.getIndex();
                    MmiPoint pointerScreenPosition = touchEvent.getPointerPosition(index);
                    HashMap<String, int[]> stringHashMap = SVGADrawable1.getDynamicItem().getmClickMap();
                    Iterator iterator = stringHashMap.entrySet().iterator();
                    while (iterator.hasNext()) {
                        Map.Entry entry = (Map.Entry) iterator.next();
                        String key = (String) entry.getKey();
                        int[] value = (int[]) entry.getValue();
                        if (pointerScreenPosition.getX() >= (float) value[0]
                                && pointerScreenPosition.getX() <= (float) value[2]
                                && pointerScreenPosition.getY() >= (float) value[1]
                                && pointerScreenPosition.getY() <= (float) value[3]) {
                            SVGAClickAreaListener svgaClickAreaListener = this.mItemClickAreaListener;
                            if (svgaClickAreaListener != null) {
                                svgaClickAreaListener.onClick(key);
                                return true;
                            }
                        }
                    }
                    return true;
                }
                return true;
            }
        }
        return true;
    }

}
